﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Irony.Parsing;

namespace NMF.Languages.AnyText
{
    [Language("AnyText", "0.1", "AnyText")]
    public class AnyTextInternalGrammar : AnyTextGrammar
    {
        private static AnyTextInternalGrammar instance;
        public static AnyTextInternalGrammar Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new AnyTextInternalGrammar();
                }
                return instance;
            }
        }

        internal readonly NonTerminal language;
        internal readonly NonTerminal languageExtension_opt;
        internal readonly NonTerminal languageMember;
        internal readonly NonTerminal languageMembers;

        internal readonly NonTerminal rule;
        internal readonly NonTerminal ruleImplementations;
        internal readonly NonTerminal ruleImplementation;

        internal readonly NonTerminal rulePart;
        internal readonly NonTerminal identifier;
        internal readonly NonTerminal reference;
        internal readonly NonTerminal keyword;
        internal readonly NonTerminal paranthesis;
        internal readonly NonTerminal plus;
        internal readonly NonTerminal star;
        internal readonly NonTerminal qmark;
        internal readonly NonTerminal typeRef;

        internal readonly NonTerminal inherited;
        internal readonly NonTerminal property;

        internal readonly NonTerminal type;
        internal readonly NonTerminal typeExpression;
        internal readonly NonTerminal ruleReference;
        internal readonly NonTerminal constant;

        internal readonly NonTerminal integer;
        internal readonly NonTerminal integerOptions_opt;
        internal readonly NonTerminal integerOptions;
        internal readonly NonTerminal integerOption;
        internal readonly NonTerminal intMaximum;
        internal readonly NonTerminal intMinimum;

        internal readonly NonTerminal @float;
        internal readonly NonTerminal floatOptions_opt;
        internal readonly NonTerminal floatOptions;
        internal readonly NonTerminal floatOption;
        internal readonly NonTerminal floatMaximum;
        internal readonly NonTerminal floatMinimum;

        internal readonly NonTerminal @string;
        internal readonly NonTerminal stringOptions_opt;
        internal readonly NonTerminal stringOptions;
        internal readonly NonTerminal stringOption;
        internal readonly NonTerminal stringMaxLength;

        internal readonly NonTerminal @enum;
        internal readonly NonTerminal enumOptions;
        internal readonly NonTerminal enumOption;
        internal readonly NonTerminal enumIgnoreCase;

        internal readonly NonTerminal boolean;
        internal readonly NonTerminal booleanOptions_opt;
        internal readonly NonTerminal booleanOptions;
        internal readonly NonTerminal booleanOption;
        internal readonly NonTerminal booleanTrueString;
        internal readonly NonTerminal booleanFalseString;

        internal readonly NonTerminal regex;
        internal readonly NonTerminal regexOptions;
        internal readonly NonTerminal regexOption;
        internal readonly NonTerminal regexPattern;
        internal readonly NonTerminal regexMultiline;

        internal readonly NonTerminal identifierDefaultsSection;
        internal readonly NonTerminal identifierDefaults;

        internal readonly NonTerminal comments;
        internal readonly NonTerminal singleLineComment;
        internal readonly NonTerminal multiLineComment;

        internal readonly AnyText.Identifier ruleName;
        internal readonly IdentifierReference ruleNameReference;
        internal readonly AnyText.Identifier typeName;
        internal readonly IdentifierReference typeNameReference;

        internal readonly KeyTerm assign;
        internal readonly KeyTerm increment;

        public AnyTextInternalGrammar() 
        {
            var lang = this;

            var name = TerminalFactory.CreateCSharpIdentifier("Name");

            ruleName = new Identifier("Rule", name);
            ruleNameReference = new IdentifierReference("RuleReference", name);
            typeName = new Identifier("Type", name);
            typeNameReference = new IdentifierReference("Type", name);

            var floatNumber = Float;
            var intNumber = Integer;
            var literal = String;
            
            KeyTerm comma = lang.ToTerm(",");
            KeyTerm colon = lang.ToTerm(":");
            assign = lang.ToTerm("=");
            increment = lang.ToTerm("+=");
            KeyTerm Lbr = lang.ToTerm("{");
            KeyTerm Rbr = lang.ToTerm("}");
            KeyTerm Lpar = lang.ToTerm("(");
            KeyTerm Rpar = lang.ToTerm(")");
            KeyTerm Or = lang.ToTerm("|");

            language = new NonTerminal("Language");
            languageExtension_opt = new NonTerminal("LanguageExtension");
            languageMember = new NonTerminal("LanguageMember");
            languageMembers = new NonTerminal("LanguageMembers");

            rule = new NonTerminal("Rule");
            rulePart = new NonTerminal("RulePart");
            paranthesis = new NonTerminal("Paranthesis");
            plus = new NonTerminal("PlusRule");
            star = new NonTerminal("StarRule");
            qmark = new NonTerminal("QuestionmarkRule");
            ruleImplementations = new NonTerminal("RuleImplementations");
            ruleImplementation = new NonTerminal("RuleImplementation");
            identifier = new NonTerminal("Identifier");
            reference = new NonTerminal("Reference");
            keyword = new NonTerminal("Keyword");
            typeRef = new NonTerminal("TypeReference");

            property = new NonTerminal("Property");
            inherited = new NonTerminal("Inheritance");

            type = new NonTerminal("Type");

            typeExpression = new NonTerminal("TypeExpression");
            ruleReference = new NonTerminal("RuleReference");
            constant = new NonTerminal("Constant");

            integer = new NonTerminal("Integer");
            integerOptions_opt = new NonTerminal("IntegerOptions_opt");
            integerOptions = new NonTerminal("IntegerOptions");
            integerOption = new NonTerminal("IntegerOption");
            intMaximum = new NonTerminal("IntegerMaximum");
            intMinimum = new NonTerminal("IntegerMinimum");

            @float = new NonTerminal("Float");
            floatOptions_opt = new NonTerminal("FloatOptions_opt");
            floatOptions = new NonTerminal("FloatOptions");
            floatOption = new NonTerminal("FloatOption");
            floatMaximum = new NonTerminal("FloatMaximum");
            floatMinimum = new NonTerminal("FloatMinimum");

            @string = new NonTerminal("String");
            stringOptions_opt = new NonTerminal("StringOptions_opt");
            stringOptions = new NonTerminal("StringOptions");
            stringOption = new NonTerminal("StringOption");
            stringMaxLength = new NonTerminal("StringMaxLength");

            @enum = new NonTerminal("Enum");
            enumOptions = new NonTerminal("EnumOptions");
            enumOption = new NonTerminal("EnumOption");
            enumIgnoreCase = new NonTerminal("IgnoreCase");

            boolean = new NonTerminal("Boolean");
            booleanOptions_opt = new NonTerminal("BooleanOptions_opt");
            booleanOptions = new NonTerminal("BooleanOptions");
            booleanOption = new NonTerminal("BooleanOption");
            booleanTrueString = new NonTerminal("BooleanTrueString");
            booleanFalseString = new NonTerminal("BooleanFalseString");

            regex = new NonTerminal("Regex");
            regexOptions = new NonTerminal("RegexOptions");
            regexOption = new NonTerminal("RegexOption");
            regexPattern = new NonTerminal("RegexPattern");
            regexMultiline = new NonTerminal("RegexMultiline");

            identifierDefaultsSection = new NonTerminal("IdentifierDefaultsSection");
            identifierDefaults = new NonTerminal("IdentifierDefaults");

            comments = new NonTerminal("Comments");
            singleLineComment = new NonTerminal("SingleLineComment");
            multiLineComment = new NonTerminal("MultiLineComment");

            language.Rule = lang.ToTerm("language") + name + languageExtension_opt + Lbr + languageMembers + Rbr;
            language.ErrorRule = lang.ToTerm("language") + name + languageExtension_opt + Lbr + lang.SyntaxError + Rbr;

            languageExtension_opt.Rule = lang.Empty | (lang.ToTerm("extension") + literal);
            languageMembers.Rule = lang.MakeStarRule(languageMembers, null, languageMember);
            languageMember.Rule = rule | type | identifierDefaultsSection | comments;

            rule.Rule = lang.ToTerm("rule") + ruleName + Lbr + ruleImplementations + Rbr;
            rule.ErrorRule = lang.ToTerm("rule") + ruleName + Lbr + lang.SyntaxError + Rbr;

            ruleImplementations.Rule = lang.MakePlusRule(ruleImplementations, Or, ruleImplementation);
            ruleImplementation.Rule = lang.MakeStarRule(ruleImplementation, null, rulePart);
            rulePart.Rule = keyword | literal | property | paranthesis | plus | star | qmark | ruleNameReference | typeExpression | inherited;

            keyword.Rule = lang.ToTerm("keyword") + Lpar + name + Rpar;
            identifier.Rule = lang.ToTerm("identifier") + Lpar + name + Rpar;
            reference.Rule = lang.ToTerm("reference") + Lpar + name + Rpar;
            paranthesis.Rule = Lpar + ruleImplementations + Rpar;
            plus.Rule = rulePart + lang.ToTerm("+");
            star.Rule = rulePart + lang.ToTerm("*");
            qmark.Rule = rulePart + lang.ToTerm("?");

            property.Rule = (name + assign + typeExpression)
                | (name + increment + typeExpression);

            inherited.Rule = lang.ToTerm("is") + Lpar + ruleNameReference + Rpar;

            ruleReference.Rule = lang.ToTerm("rule") + Lpar + ruleNameReference + Rpar;

            type.Rule = lang.ToTerm("type") + typeName + colon + typeExpression;

            typeExpression.Rule = integer
                | @float
                | @string
                | regex
                | @enum
                | boolean
                | typeRef
                | constant
                | identifier
                | reference
                | ruleReference;

            constant.Rule = lang.ToTerm("constant") + Lpar + (literal | intNumber | floatNumber | Boolean) + Rpar;

            typeRef.Rule = lang.ToTerm("type") + Lpar + typeNameReference + Rpar;

            integer.Rule = lang.ToTerm("integer") + integerOptions_opt;
            integerOptions_opt.Rule = lang.Empty | Lbr + integerOptions + Rbr;
            integerOptions_opt.ErrorRule = Lbr + lang.SyntaxError + Rbr;
            integerOptions.Rule = lang.MakeStarRule(integerOptions, comma, integerOption);
            integerOption.Rule = intMaximum | intMinimum;
            intMaximum.Rule = lang.ToTerm("maximum") + assign + intNumber;
            intMinimum.Rule = lang.ToTerm("minimum") + assign + intNumber;

            @float.Rule = lang.ToTerm("float") + floatOptions_opt;
            floatOptions_opt.Rule = lang.Empty | Lbr + floatOptions + Rbr;
            floatOptions_opt.ErrorRule = lang.Empty | Lbr + lang.SyntaxError + Rbr;
            floatOptions.Rule = lang.MakeStarRule(floatOptions, comma, floatOption);
            floatOption.Rule = floatMinimum | floatMaximum;
            floatMaximum.Rule = lang.ToTerm("maximum") + assign + floatNumber;
            floatMinimum.Rule = lang.ToTerm("minimum") + assign + floatNumber;

            @string.Rule = lang.ToTerm("string") + stringOptions_opt;
            stringOptions_opt.Rule = lang.Empty | Lbr + stringOptions + Rbr;
            stringOptions_opt.ErrorRule = lang.Empty | Lbr + lang.SyntaxError + Rbr;
            stringOptions.Rule = lang.MakeStarRule(stringOptions, comma, stringOption);
            stringOption.Rule = stringMaxLength;
            stringMaxLength.Rule = lang.ToTerm("maxLength") + assign + intNumber;

            @enum.Rule = lang.ToTerm("enum") + Lbr + enumOptions + Rbr;
            @enum.ErrorRule = lang.ToTerm("enum") + Lbr + lang.SyntaxError + Rbr;
            enumOptions.Rule = lang.MakePlusRule(enumOptions, comma, enumOption);
            enumOption.Rule = enumIgnoreCase | literal;
            enumIgnoreCase.Rule = lang.ToTerm("ignoreCase");

            regex.Rule = lang.ToTerm("regex") + Lbr + regexOptions + Rbr;
            regex.ErrorRule = lang.ToTerm("regex") + Lbr + lang.SyntaxError + Rbr;
            regexOptions.Rule = lang.MakeStarRule(regexOptions, comma, regexOption);
            regexOption.Rule = stringOption | regexPattern;
            regexPattern.Rule = lang.ToTerm("pattern") + assign + literal;

            boolean.Rule = lang.ToTerm("boolean") + booleanOptions_opt;
            booleanOptions_opt.Rule = lang.Empty | Lbr + booleanOptions + Rbr;
            booleanOptions_opt.ErrorRule = lang.Empty | Lbr + lang.SyntaxError + Rbr;
            booleanOptions.Rule = lang.MakeStarRule(booleanOptions, comma, booleanOption);
            booleanOption.Rule = booleanFalseString | booleanTrueString;
            booleanTrueString.Rule = lang.ToTerm("true") + assign + literal;
            booleanFalseString.Rule = lang.ToTerm("false") + assign + literal;

            identifierDefaultsSection.Rule = lang.ToTerm("identifier") + name + Lbr + identifierDefaults + Rbr;
            identifierDefaultsSection.ErrorRule = lang.ToTerm("identifier") + name + Lbr + lang.SyntaxError + Rbr;
            identifierDefaults.Rule = lang.MakeStarRule(identifierDefaults, comma, literal);

            comments.Rule = singleLineComment | multiLineComment;
            singleLineComment.Rule = lang.ToTerm("comment") + Lpar + literal + Rpar;
            multiLineComment.Rule = lang.ToTerm("comment") + Lpar + literal + comma + literal + Rpar;

            lang.MarkReservedWords("language", "extension", "rule", "keyword", "identifier", "reference", "type",
                "comment", "integer", "float", "boolean", "enum", "regex", "string", 
                "true", "false", "ignoreCase", "minimum", "maximum", "maxLength", "pattern", "constant", "is");

            this.Delimiters = "{}(),:;+*|=";

            this.AddTermsReportGroup("assignment", "=", "+=");
            this.AddTermsReportGroup("typename", "integer", "float", "enum", "string", "regex", "boolean");

            this.Identifiers.Add(ruleName);
            this.Identifiers.Add(typeName);
            this.References.Add(ruleNameReference);
            this.References.Add(typeNameReference);

            lang.Root = language;
            
        }
    }
}
